{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "d04af312",
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d77412fe",
   "metadata": {},
   "source": [
    "需要求导的，可以手动定义："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "b6aad92e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[ 0.9395,  0.3111,  0.2878,  0.2753],\n",
       "        [ 1.5783, -0.6051, -1.5767,  1.6654],\n",
       "        [-0.7055,  0.1284, -0.3852,  1.7704]], requires_grad=True)"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 方法1\n",
    "x = torch.randn(3, 4, requires_grad=True)\n",
    "x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "101416e5",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[-0.4975, -0.6126, -0.6829,  0.8081],\n",
       "        [-1.6153, -0.1069,  0.2327, -0.7320],\n",
       "        [ 0.8437,  0.4139, -1.9289,  1.3078]], requires_grad=True)"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 方法2\n",
    "x = torch.randn(3, 4)\n",
    "x.requires_grad=True\n",
    "x"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "13774d75",
   "metadata": {},
   "source": [
    "#### 求导举例"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "de2cccc7",
   "metadata": {},
   "outputs": [],
   "source": [
    "b = torch.randn(3, 4, requires_grad=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "13e5293b",
   "metadata": {},
   "outputs": [],
   "source": [
    "t = x + b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "72458aa8",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor(-0.7440, grad_fn=<SumBackward0>)"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "y = t.sum()\n",
    "y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "3052e358",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 反向传播\n",
    "y.backward()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "a78d2cc5",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[1., 1., 1., 1.],\n",
       "        [1., 1., 1., 1.],\n",
       "        [1., 1., 1., 1.]])"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "b.grad"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7c072812",
   "metadata": {},
   "source": [
    "###### 虽然没有指定t的requires_grad，但是需要用到它，也会默认设置"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "4b5fba51",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(True, True, True)"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x.requires_grad, b.requires_grad, t.requires_grad"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fd793103",
   "metadata": {},
   "source": [
    "#### 举个例子"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "cdeca803",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 计算流程\n",
    "x = torch.rand(1)\n",
    "b = torch.rand(1, requires_grad=True)\n",
    "w = torch.rand(1, requires_grad=True)\n",
    "y = w * x\n",
    "z = y + b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "316d9153",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(False, True, True, True)"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 注意y也是需要的\n",
    "x.requires_grad, b.requires_grad, w.requires_grad, y.requires_grad"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "f4f08557",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(True, True, True, False, False)"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x.is_leaf, w.is_leaf, b.is_leaf, y.is_leaf, z.is_leaf"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1d16855c",
   "metadata": {},
   "source": [
    "###### 反向传播计算"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "a2048c3b",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 如果不清空会累加起来\n",
    "z.backward(retain_graph=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "ed0ff4fd",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([0.7060])"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 梯度值\n",
    "w.grad"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "36127cf5",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([1.])"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 梯度值\n",
    "b.grad"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "36954da9",
   "metadata": {},
   "source": [
    "## 一个线性回归模型实例\n",
    "\n",
    "构造一组输入数据X和其对应的标签y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "d3a084cd",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(11, 1)"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import numpy as np\n",
    "x_values = [i for i in range(11)]\n",
    "x_train = np.array(x_values, dtype=np.float32)\n",
    "x_train = x_train.reshape(-1, 1)\n",
    "x_train.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "d29c7269",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(11, 1)"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 构建模型：y = 2x + 1\n",
    "y_values = [2 * i + 1 for i in x_values]\n",
    "y_train = np.array(y_values, dtype=np.float32)\n",
    "y_train = y_train.reshape(-1, 1)\n",
    "y_train.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "2fed6caf",
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "import torch.nn as nn"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "22de1e30",
   "metadata": {},
   "source": [
    "#### 线性回归模型\n",
    "\n",
    "- 其实线性回归就是一个不加激活函数的全连接层"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "ca1257c4",
   "metadata": {},
   "outputs": [],
   "source": [
    "class LinearRegressionModel(nn.Module):\n",
    "    def __init__(self, input_dim, output_dim):\n",
    "        super(LinearRegressionModel, self).__init__()\n",
    "        # 自定义用到的层\n",
    "        self.linear = nn.Linear(input_dim, output_dim)\n",
    "    \n",
    "    def forward(self, x):\n",
    "        out = self.linear(x)\n",
    "        return out"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "f3a731e4",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "LinearRegressionModel(\n",
       "  (linear): Linear(in_features=1, out_features=1, bias=True)\n",
       ")"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "input_dim = 1\n",
    "output_dim = 1\n",
    "\n",
    "model = LinearRegressionModel(input_dim, output_dim)\n",
    "\n",
    "model\n",
    "# 全连接层\n",
    "# Linear(in_features=1, out_features=1, bias=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4d22c7f8",
   "metadata": {},
   "source": [
    "指定好参数和损失函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "6da47e9e",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 开始训练\n",
    "\n",
    "# 学习次数\n",
    "epochs = 1000\n",
    "# 学习率\n",
    "learning_rate = 0.01\n",
    "# 指定SGD优化器\n",
    "optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)\n",
    "# 损失函数\n",
    "criterion = nn.MSELoss()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0dbe4315",
   "metadata": {},
   "source": [
    "训练模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "5db44fea",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "epoch 50, loss 0.4705349802970886\n",
      "epoch 100, loss 0.2683757543563843\n",
      "epoch 150, loss 0.15307138860225677\n",
      "epoch 200, loss 0.08730615675449371\n",
      "epoch 250, loss 0.04979623854160309\n",
      "epoch 300, loss 0.02840195968747139\n",
      "epoch 350, loss 0.01619935780763626\n",
      "epoch 400, loss 0.009239563718438148\n",
      "epoch 450, loss 0.005269866902381182\n",
      "epoch 500, loss 0.003005748149007559\n",
      "epoch 550, loss 0.0017143461154773831\n",
      "epoch 600, loss 0.0009777972009032965\n",
      "epoch 650, loss 0.0005577061092481017\n",
      "epoch 700, loss 0.00031809622305445373\n",
      "epoch 750, loss 0.00018142205954063684\n",
      "epoch 800, loss 0.00010347810894018039\n",
      "epoch 850, loss 5.901998156332411e-05\n",
      "epoch 900, loss 3.366298187756911e-05\n",
      "epoch 950, loss 1.9201186660211533e-05\n",
      "epoch 1000, loss 1.0951614967780188e-05\n"
     ]
    }
   ],
   "source": [
    "for epoch in range(epochs):\n",
    "    epoch += 1\n",
    "    # 注意转换成tensor\n",
    "    inputs = torch.from_numpy(x_train)\n",
    "    labels = torch.from_numpy(y_train)\n",
    "    \n",
    "    # 梯度要清零每一次迭代\n",
    "    optimizer.zero_grad()\n",
    "    \n",
    "    # 前向传播\n",
    "    outputs = model(inputs)\n",
    "    \n",
    "    # 计算损失\n",
    "    loss = criterion(outputs, labels)\n",
    "    \n",
    "    # 反向传播\n",
    "    loss.backward()\n",
    "    \n",
    "    # 更新权重参数\n",
    "    optimizer.step()\n",
    "    if epoch % 50 == 0:\n",
    "        print('epoch {}, loss {}'.format(epoch, loss.item()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1270f363",
   "metadata": {},
   "source": [
    "#### 测试模型预测结果"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "57142e29",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 0.9938439],\n",
       "       [ 2.9947305],\n",
       "       [ 4.995617 ],\n",
       "       [ 6.9965034],\n",
       "       [ 8.99739  ],\n",
       "       [10.998277 ],\n",
       "       [12.999163 ],\n",
       "       [15.000049 ],\n",
       "       [17.000935 ],\n",
       "       [19.001822 ],\n",
       "       [21.002708 ]], dtype=float32)"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "predicted = model(torch.from_numpy(x_train).requires_grad_()).data.numpy()\n",
    "predicted"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "29d416ea",
   "metadata": {},
   "source": [
    "#### 模型的保存与读取"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6a266f55",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 保存模型\n",
    "torch.save(model.state_dict(), 'model.pkl')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1630d730",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 读取模型\n",
    "model.load_state_dict(torch.load('model.pkl'))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "20266bc0",
   "metadata": {},
   "source": [
    "#### 使用GPU进行训练\n",
    "\n",
    "- 只需要把数据和模型传入到cuda里面就可以了"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "id": "d4ceca23",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "epoch 50, loss 0.2523045241832733\n",
      "epoch 100, loss 0.14390498399734497\n",
      "epoch 150, loss 0.08207792788743973\n",
      "epoch 200, loss 0.04681418091058731\n",
      "epoch 250, loss 0.026700951159000397\n",
      "epoch 300, loss 0.015229273587465286\n",
      "epoch 350, loss 0.00868619792163372\n",
      "epoch 400, loss 0.00495430501177907\n",
      "epoch 450, loss 0.002825710689648986\n",
      "epoch 500, loss 0.0016116806073114276\n",
      "epoch 550, loss 0.0009192371508106589\n",
      "epoch 600, loss 0.0005243075429461896\n",
      "epoch 650, loss 0.00029905018163844943\n",
      "epoch 700, loss 0.00017056406068149954\n",
      "epoch 750, loss 9.727844008011743e-05\n",
      "epoch 800, loss 5.5483262258348987e-05\n",
      "epoch 850, loss 3.164942972944118e-05\n",
      "epoch 900, loss 1.8050339349429123e-05\n",
      "epoch 950, loss 1.029522991302656e-05\n",
      "epoch 1000, loss 5.871451776329195e-06\n"
     ]
    }
   ],
   "source": [
    "import torch\n",
    "import torch.nn as nn\n",
    "import numpy as np\n",
    "\n",
    "class LinearRegressionModel(nn.Module):\n",
    "    def __init__(self, input_dim, output_dim):\n",
    "        super(LinearRegressionModel, self).__init__()\n",
    "        # 自定义用到的层\n",
    "        self.linear = nn.Linear(input_dim, output_dim)\n",
    "    \n",
    "    def forward(self, x):\n",
    "        out = self.linear(x)\n",
    "        return out\n",
    "\n",
    "input_dim = 1\n",
    "output_dim = 1\n",
    "\n",
    "model = LinearRegressionModel(input_dim, output_dim)\n",
    "\n",
    "# 选择GPU或CPU\n",
    "device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')\n",
    "model.to(device)\n",
    "\n",
    "criterion = nn.MSELoss()\n",
    "\n",
    "learning_rate = 0.01\n",
    "\n",
    "optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)\n",
    "\n",
    "epochs = 1000\n",
    "for epoch in range(epochs):\n",
    "    epoch += 1\n",
    "    # 注意转换成tensor\n",
    "    inputs = torch.from_numpy(x_train).to(device)\n",
    "    labels = torch.from_numpy(y_train).to(device)\n",
    "    \n",
    "    # 梯度要清零每一次迭代\n",
    "    optimizer.zero_grad()\n",
    "    \n",
    "    # 前向传播\n",
    "    outputs = model(inputs)\n",
    "    \n",
    "    # 计算损失\n",
    "    loss = criterion(outputs, labels)\n",
    "    \n",
    "    # 反向传播\n",
    "    loss.backward()\n",
    "    \n",
    "    # 更新权重参数\n",
    "    optimizer.step()\n",
    "    if epoch % 50 == 0:\n",
    "        print('epoch {}, loss {}'.format(epoch, loss.item()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d47dacca",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
